/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pos.Driving.Path.Segment;

import pos.Driving.Path.*;
import java.util.*;
import javax.media.j3d.*;
import javax.vecmath.*;
import pos.Driving.Train.Train;
import pos.utils.ObjectFactory;

/**
 *
 * @author Administrator
 */
public abstract class Segment extends BranchGroup {
    
    private class PathSection {
        private double start,  dest;
        //private boolean visuable;
        public Followable path;

        public double getDest() {
            return dest;
        }

        public double getStart() {
            return start;
        }

        public PathSection(double start, double dest, Followable path) {
            this.start = start;
            this.dest = dest;
            //this.visuable = isVisual;
            this.path = path;
        }

        public boolean isInRange(double D) {
            return (D >= start && D < dest);
        }

        /*public boolean isVisuable() {
        return visuable;
        }*/
    }
    // private final static int STATUE_CURRENT = 0,  STATUE_PAST = -1,  STATUE_PRELOAD = 1;
    private int id;
    private String name,  desc,  surrendingKey;
    private Point3d startPoint;
    private HashMap<Integer, ArrayList<PathSection>> rails
            = new HashMap<Integer, ArrayList<PathSection>>();
    private double Length;  //长度

    private BranchGroup sectBack,  sectCurrent,  sectForward;
    private int sectID = -1;
    //private RailModelFactory RailFactory;
    private final int sectionLength = 300;
    protected HashMap<Integer, ArrayList<Attachable>> onRailDevices
            = new HashMap<Integer, ArrayList<Attachable>>();
    protected ArrayList<HashMap<String, Sideway>> sideways
            = new ArrayList<HashMap<String, Sideway>>();
    
    protected Segment(int id, String name,
            String desc, String surrendingKey, Point3d startPoint) {
        this.setCapability(ALLOW_DETACH);
        this.id = id;
        this.name = name;
        this.desc = desc;
        this.surrendingKey = surrendingKey;
        this.startPoint = startPoint;
        if (this.surrendingKey != null) {
            addChild(ObjectFactory.createObject(this.surrendingKey));                    //加入环境模型
        }
        this.setCapability(Group.ALLOW_CHILDREN_EXTEND);
        this.setCapability(Group.ALLOW_CHILDREN_WRITE);
        initRail();
    }

    protected abstract void initRail();

    protected void addPathSection(int pathID, double startD, double destD,
            Followable pathSection) {
        if (rails.get(pathID) == null) {
            rails.put(pathID, new ArrayList<PathSection>());
        }
        rails.get(pathID).add(new PathSection(startD, destD, pathSection));
    }

    protected void addOnRailDevice(int pathID, double localD, Attachable device) {
        PosPoint p = new PosPoint();
        p.d = localD;
        p.path_ID = pathID;
        p = getPosPoint(p);
        device.setLocation(localD, p);
        if (onRailDevices.get(pathID) == null) {
            onRailDevices.put(pathID, new ArrayList<Attachable>());
        }
        onRailDevices.get(pathID).add(device);
        this.addChild((Node) device);
    }
    
    protected void addSideway(int pathID, double localD, String desc, Sideway device) {
        PosPoint p = new PosPoint();
        p.d = localD;
        p.path_ID = pathID;
        p = getPosPoint(p);
        device.setLocation(localD,p);
            if (sideways.size()<(pathID+1)) {
                sideways.add(new HashMap<String,Sideway>());
            }
            sideways.get(pathID).put(desc, device);
        this.addChild((Node) device);
    }
    
    protected void sidewayCheck(PosPoint p){
        HashMap<String,Sideway> pathSideways;
        try{
            pathSideways= sideways.get(p.path_ID);
        }catch(Exception e){
            return;
        }
        if (pathSideways==null)
            return;
        for (Sideway sideway: pathSideways.values()){
            sideway.choosePath(p);
        }
    }
    
    public PosPoint getPosPoint(PosPoint pos) {
        ArrayList<PathSection> path = rails.get(pos.path_ID);
        if (path == null) {
            return pos;                                           //所查询的路轨不存在，返回原位置

        }
        for (PathSection sect : path) {
            if (sect.isInRange(pos.d)) {
                PosPoint res = sect.path.makePath(pos);
                res.path_ID=pos.path_ID;
                return res;
            }
        }
        return pos;                                                             //所查询的路轨不存在，返回原位置

    }

    public PosPoint refreshPosition(PosPoint pos) {
        sidewayCheck(pos);                                      //道岔检查
        ArrayList<PathSection> path = rails.get(pos.path_ID);
        if (path == null) {
            return pos;                                                          //所查询的路轨不存在，返回原位置

        }
        for (PathSection sect : path) {
            if (sect.isInRange(pos.d)) {
                return sect.path.makePath(pos);
            }
        }
        return pos;                                                             //所查询的路轨不存在，返回原位置

    }

    public void trainAction(Train train) {
        PosPoint pos=train.getTrainPos();
        sidewayCheck(pos);                                      //道岔检查
        train.setTrainPath(pos.path_ID);
        
        ArrayList<Attachable> devices = onRailDevices.get(pos.path_ID);
        if (devices != null) {
            for (Attachable dev : devices) {
                if (dev.test(pos.d)) {
                    dev.action(train);
                }
            }
        }
        sectionTest(pos.d);
        //System.out.println(pos.d);
    }
    
    public boolean setSideway(SidewayDesc desc){
        if (sideways.get(desc.pathID)==null) return false;
        Sideway target=sideways.get(desc.pathID).get(desc.desc);
        if (target == null)
            return false;
        else{
            target.setIsSidewayA(desc.sideA);
            target.setIsSidewayB(desc.sideB);
            return true;
        }
    }
    /**
     * 动态轨道及周边模型生成函数
     * @param D 当前列车位置
     */
    private void sectionTest(final double D) {
        final int newID = (int) D / sectionLength;
        if ((newID - sectID) == 1) {
            new Timer().schedule(new TimerTask() {

                public void run() {
                    sectForward(newID);
                }
            }, 0);
        } else if ((newID - sectID) == -1) {
            new Timer().schedule(new TimerTask() {

                public void run() {
                    sectBackward(newID);
                }
            }, 0);
        } else if (newID != sectID) {
            new Timer().schedule(new TimerTask() {

                public void run() {
                    sectReset(newID);
                }
            }, 0);
        }
        sectID = newID;
    }

    private void sectForward(final int id) {
        if (sectBack != null) {
            sectBack.detach();
            sectBack = null;
        }
        if (sectCurrent != null) {
            sectBack = sectCurrent;
        } else {
            sectCurrent = getSection(id * sectionLength, (id + 1) * sectionLength);
            addChild(sectCurrent);
        }
        if (sectForward != null) {
            sectCurrent = sectForward;
        }
        sectForward = getSection((id + 1) * sectionLength, (id + 2) * sectionLength);
        addChild(sectForward);
    }

    private void sectBackward(final int id) {
        if (sectForward != null) {
            sectForward.detach();
            sectForward = null;
        }
        if (sectCurrent != null) {
            sectForward = sectCurrent;
        } else {
            sectCurrent = getSection(id * sectionLength, (id + 1) * sectionLength);
            addChild(sectCurrent);
        }
        if (sectBack != null) {
            sectCurrent = sectBack;
        }
        sectBack = getSection((id - 1) * sectionLength, id * sectionLength);
        addChild(sectBack);
    }

    private void sectReset(final int id) {
        if (sectForward != null) {
            sectForward.detach();
            sectForward = null;
        }
        if (sectCurrent != null) {
            sectCurrent.detach();
            sectCurrent = null;
        }
        if (sectBack != null) {
            sectBack.detach();
            sectBack = null;
        }
        sectBack = getSection((id - 1) * sectionLength, id * sectionLength);
        sectCurrent = getSection(id * sectionLength, (id + 1) * sectionLength);
        sectForward = getSection((id + 1) * sectionLength, (id + 2) * sectionLength);
        addChild(sectBack);
        addChild(sectCurrent);
        addChild(sectForward);
    }

    private BranchGroup getSection(double start, double dest) {
        BranchGroup BGResult = new BranchGroup();
        BGResult.setCapability(BranchGroup.ALLOW_DETACH);

        for (int pathCounter = 0; pathCounter < rails.size(); pathCounter++) {
            ArrayList<PathSection> path = rails.get(pathCounter);
            if (path != null) {
                for (PathSection sect : path) {
                    if (sect.getStart() <= start && sect.getDest() >= dest) // own the section
                    {
                        BGResult.addChild(SectionFactory.MakeSection(
                                this, pathCounter, start, dest));
                    } else if (sect.getStart() >= start && sect.getDest() >= dest) {
                        BGResult.addChild(SectionFactory.MakeSection(
                                this, pathCounter, sect.getStart(), dest));
                    } else if (sect.getStart() <= start && sect.getDest() < dest) {
                        BGResult.addChild(SectionFactory.MakeSection(
                                this, pathCounter, start, sect.getDest()));
                    } else if (sect.getStart() >= start && sect.getDest() < dest) // inside the section
                    {
                        BGResult.addChild(SectionFactory.MakeSection(
                                this, pathCounter, sect.getStart(), sect.getDest()));
                    }
                }
            }
        }
        return BGResult;
    }

    public String getDesc() {
        return desc;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }
}
